#!/usr/bin/env python
# -*- coding: utf-8 -*-
from applications.models.transaction import Transaction, Category, Icon
from applications.extensions.jwt_plugin import jwt_required_with_user
from flask import Blueprint, request, jsonify, g, current_app
from applications.extensions.db_plugin import db
from sqlalchemy import and_, or_, extract, func, case
from datetime import datetime, date, timedelta
from common.utils.helpers import now_beijing
from common.core.make_response import (
    make_success, make_server_error, make_validation_error,
    make_not_found, make_forbidden, make_unauthorized
)
from decimal import Decimal

trans_bp = Blueprint('trans', __name__, url_prefix='/api/transactions')


@trans_bp.route('/search', methods=['GET'])
@jwt_required_with_user()
def search_transaction():
    """搜索账单，支持 start_date / end_date / category_id 查询"""
    try:
        user_id = g.user.id

        q_start_date = request.args.get('start_date', '').strip()
        q_end_date = request.args.get('end_date', '').strip()
        category_id = request.args.get('category_id', '').strip()

        current_app.logger.info(
            f"用户【{user_id}】搜索账单，参数：start_date={q_start_date}, end_date={q_end_date}, category_id={category_id}")

        # 解析日期参数
        def parse_date(s):
            try:
                return datetime.strptime(s, "%Y-%m-%d").date()
            except ValueError:
                current_app.logger.error(f"查询账单日期转换异常: {s}")
                return None

        # 必须提供开始和结束日期
        if not q_start_date or not q_end_date:
            return make_validation_error(errors={'date': '必须提供开始日期和结束日期'})

        start_date = parse_date(q_start_date)
        end_date = parse_date(q_end_date)

        if not start_date or not end_date:
            return make_validation_error(errors={'date': '日期格式错误，应为 YYYY-MM-DD'})

        # 验证日期范围合理性
        if start_date > end_date:
            return make_validation_error(errors={'date': '开始日期不能晚于结束日期'})

        # 构建查询条件
        query_filters = [
            Transaction.user_id == user_id,
            Transaction.is_delete.is_(False),
            Transaction.date >= start_date,
            Transaction.date <= end_date
        ]

        # 添加分类筛选
        if category_id:
            try:
                category_id_int = int(category_id)
                query_filters.append(Transaction.category_id == category_id_int)
            except (ValueError, TypeError):
                return make_validation_error(errors={'category_id': '无效的 category_id'})

        # 执行查询
        transactions = Transaction.query.filter(
            and_(*query_filters)
        ).order_by(Transaction.date.desc()).all()

        # 如果没有数据，直接返回空结果和零统计
        if not transactions:
            current_app.logger.info(f"用户【{user_id}】搜索账单成功，无数据")
            return make_success(
                data=[],
                extra={
                    "totalRevenue": 0.0,
                    "totalExpenditure": 0.0,
                    "totalBalance": 0.0
                }
            )

        # 获取分类信息
        try:
            categories = Category.query.filter(
                or_(
                    Category.user_id == user_id,
                    Category.user_id == 0
                ),
                Category.is_delete.is_(False)
            ).all()

            category_map = {
                c.id: {
                    "id": c.id,
                    "name": c.name,
                    "icon": c.icon_class,
                    "color": c.icon_color
                } for c in categories
            }
        except Exception as e:
            current_app.logger.error(f"获取分类信息失败: {str(e)}")
            category_map = {}

        # 按日期分组处理结果
        from collections import defaultdict
        grouped = defaultdict(list)
        weekday_names = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]

        # 计算总收入、总支出
        total_revenue = Decimal('0')
        total_expenditure = Decimal('0')

        for tx in transactions:
            try:
                date_str = tx.date.isoformat()
                date_obj = tx.date
                weekday = weekday_names[date_obj.weekday()]
                grouped_key = f"{date_str}_{weekday}"
                grouped[grouped_key].append(tx)

                # 累计总收入和总支出
                amount = Decimal(str(tx.amount))
                if tx.type == 0:
                    total_revenue += amount
                else:
                    total_expenditure += amount
            except (ValueError, TypeError, AttributeError) as e:
                current_app.logger.error(f"处理交易记录时出错: {str(e)}, 交易ID: {tx.id if tx else 'Unknown'}")
                continue

        # 计算结余
        total_balance = total_revenue - total_expenditure

        # 构建返回结果
        merged_result = []
        for key, items in grouped.items():
            try:
                date_str, weekday = key.split('_', 1)
                revenue = Decimal('0')
                expenditure = Decimal('0')
                infos = []

                for i in items:
                    try:
                        amount = Decimal(str(i.amount))
                        if i.type == 0:
                            revenue += amount
                        else:
                            expenditure += amount

                        category_info = category_map.get(i.category_id)
                        if not category_info:
                            category_info = {
                                "id": i.category_id,
                                "name": "未知分类",
                                "icon": "bi-question-circle",
                                "color": "text-muted"
                            }

                        infos.append({
                            "date": date_str,
                            "desc": i.description or "",
                            "id": i.id,
                            "userId": i.user_id,
                            "paymentType": i.type,
                            "amount": float(amount),
                            "type": i.type,
                            "categoryInfo": category_info
                        })
                    except (ValueError, TypeError, AttributeError) as e:
                        current_app.logger.error(f"处理交易明细时出错: {str(e)}, 交易ID: {i.id if i else 'Unknown'}")
                        continue

                merged_result.append({
                    "date": date_str,
                    "balance": float(revenue - expenditure),
                    "expenditure": float(expenditure),
                    "revenue": float(revenue),
                    "weekday": weekday,
                    "infos": infos
                })
            except Exception as e:
                current_app.logger.error(f"构建日期分组数据时出错: {str(e)}, 分组键: {key}")
                continue

        current_app.logger.info(f"用户【{user_id}】搜索账单成功，共 {len(merged_result)} 天数据")
        # 在现有返回结构中添加扩展字段
        return make_success(
            data=merged_result,
            extra={
                "totalRevenue": float(total_revenue),
                "totalExpenditure": float(total_expenditure),
                "totalBalance": float(total_balance)
            }
        )

    except Exception as e:
        current_app.logger.error(f"搜索账单时发生未预期错误: {str(e)}")
        return make_server_error()


@trans_bp.route('/save', methods=['POST'])
@jwt_required_with_user()
def save_transaction():
    """保存账单"""
    user_id = g.user.id
    data = request.get_json()

    if not data:
        return make_validation_error(errors={'data': '请求数据为空'})

    # 必填字段校验
    required_fields = ['type', 'categoryId', 'amount', 'description', 'date']
    for field in required_fields:
        if field not in data:
            return make_validation_error(errors={field: f'{field} 不能为空'})

    try:
        # ✅ 把 date 字符串转为 datetime.date 对象（例如 '2025-06-29'）
        try:
            date_obj = datetime.strptime(data['date'], '%Y-%m-%d').date()
        except ValueError:
            return make_validation_error(errors={'date': '无效的日期'})

        # ✅ 构造并保存账单
        t = Transaction(
            user_id=user_id,
            type=int(data['type']),
            category_id=data['categoryId'],
            amount=float(data['amount']),
            description=data['description'],
            date=date_obj
        )
        db.session.add(t)
        db.session.commit()

        current_app.logger.info(f"用户 {user_id} 添加了账单: {t.description}")
        return make_success(data={'desc': t.description})

    except (ValueError, TypeError):
        db.session.rollback()
        return make_validation_error(errors={'data': '数据类型错误'})
    except Exception as e:
        db.session.rollback()
        current_app.logger.error(f"保存账单失败 - 用户: {user_id} - 错误: {str(e)}")
        return make_server_error()


@trans_bp.route('/save/<transaction_id>', methods=['PUT'])
@jwt_required_with_user()
def edit_transaction(transaction_id):
    """修改账单"""
    try:
        user_id = g.user.id
        info = Transaction.query.filter_by(id=transaction_id).first()
        if info.user_id != user_id:
            return make_forbidden(msg='无权修改该账单')
        if not info:
            return make_not_found(msg='账单不存在')

        data = request.get_json()
        required_fields = ['type', 'categoryId', 'amount', 'description', 'date']
        for field in required_fields:
            if field not in data:
                return make_validation_error(errors={field: f'缺少必要字段: {field}'})

        # ✅ 解析日期字符串
        try:
            date_obj = datetime.strptime(data['date'], '%Y-%m-%d').date()
        except ValueError:
            return make_validation_error(errors={'date': '无效的日期'})

        # ✅ 更新字段
        info.type = int(data['type'])
        info.category_id = data['categoryId']
        info.amount = float(data['amount'])
        info.description = data['description']
        info.date = date_obj
        info.update_time = now_beijing()

        db.session.commit()
        current_app.logger.info(f"修改账单【{info.id}】成功: {info.description}")
        return make_success(data={
            'id': info.id,
            'type': info.type,
            'categoryId': info.category_id,
            'amount': info.amount,
            'description': info.description,
            'date': info.date.isoformat()  # 返回 ISO 字符串给前端
        })

    except Exception as e:
        db.session.rollback()
        current_app.logger.error(f"修改账单【{transaction_id}】失败: {e}")
        return make_server_error()


@trans_bp.route('/delete/<transaction_id>', methods=['DELETE'])
@jwt_required_with_user()
def delete_transaction(transaction_id):
    """删除账单"""
    try:
        user_id = g.user.id
        if not transaction_id.isdigit():
            return make_validation_error(errors={'transaction_id': '无效的账单ID'})

        info = Transaction.query.filter_by(id=int(transaction_id)).first()
        if info.user_id != user_id:
            return make_forbidden(msg='无权删除该账单')

        if not info:
            return make_not_found(msg='账单不存在')

        if info.is_delete:
            return make_forbidden(msg='该账单已被删除')

        info.is_delete = True
        info.update_time = now_beijing()
        db.session.commit()

        desc = info.description or "无描述"
        user_id = g.user.id
        current_app.logger.info(f"用户 {user_id} 删除账单: {transaction_id}, 描述: {desc}")
        return make_success(data={'desc': info.description})

    except Exception as e:
        db.session.rollback()
        current_app.logger.error(f"删除账单【{transaction_id}】失败: {str(e)}")
        return make_server_error()


@trans_bp.route('/search/category', methods=['GET'])
@jwt_required_with_user()
def search_category():
    """搜索分类"""
    user_id = g.user.id

    try:
        # 查询当前用户的分类和系统默认分类
        categories = Category.query.filter(
            or_(
                Category.user_id == user_id,
                Category.user_id == 0
            ),
            Category.is_delete == False
        ).order_by(
            Category.update_time.desc()
        ).limit(100).all()

        result = [{
            'id': category.id,
            'userId': category.user_id,
            'name': category.name,
            'icon': category.icon_class,  # icon_class 对应图标类名
            'color': category.icon_color,
            'type': category.type
        } for category in categories]

        current_app.logger.info(f"用户 {user_id} 查询分类成功")
        return make_success(data=result)

    except db.exc.SQLAlchemyError as e:
        # 捕获数据库异常，防止服务崩溃
        current_app.logger.error(f"查询分类失败，用户ID: {user_id}: {str(e)}")
        return make_server_error()


@trans_bp.route('/save/category', methods=['POST'])
@jwt_required_with_user()
def save_category():
    """保存分类"""
    try:
        user_id = g.user.id
        data = request.get_json()

        if not data:
            return make_validation_error(errors={'data': '无效的请求数据'})

        required_fields = ['name', 'icon', 'color']
        for field in required_fields:
            if field not in data:
                return make_validation_error(errors={field: f'缺少必要字段: {field}'})

        c = Category(
            user_id=user_id,
            name=data['name'],
            icon_class=data['icon'],
            icon_color=data['color'],
            type=data['type']
        )
        db.session.add(c)
        db.session.commit()
        current_app.logger.info(f"用户 {user_id} 添加分类: {c.name}")
        return make_success(data={'id': c.id})

    except Exception as e:
        db.session.rollback()
        current_app.logger.error(f"保存分类失败: {str(e)}")
        return make_server_error()


@trans_bp.route('/save/category/<category_id>', methods=['PUT'])
@jwt_required_with_user()
def edit_category(category_id):
    """修改分类"""
    try:
        user_id = g.user.id
        # 查询分类是否存在
        info = Category.query.filter_by(id=category_id).first()
        if info.user_id != user_id:
            return make_forbidden(msg='无权修改该分类')
        if not info:
            return make_not_found(msg='分类不存在')

        # 获取请求数据
        data = request.get_json(force=False)
        if not data:
            return make_validation_error(errors={'data': '无效的请求数据'})

        # 提取数据并设置默认值（根据业务需求决定是否需要）
        name = data.get('name')
        icon_class = data.get('icon')
        icon_color = data.get('color')
        _type = data.get('type')

        # 校验必要字段
        if not all([name, icon_class, icon_color]):
            return make_validation_error(errors={'data': '缺少必要字段'})

        # 更新模型
        info.name = name
        info.icon_class = icon_class
        info.icon_color = icon_color
        info.type = _type
        info.update_time = now_beijing()

        db.session.commit()
        current_app.logger.info(f"用户 {g.user.id} 修改分类: {category_id}, 分类名: {info.name}")
        return make_success(data={'name': info.name})

    except Exception as e:
        db.session.rollback()
        current_app.logger.error(f"修改分类失败: {str(e)}")
        return make_server_error()


@trans_bp.route('/delete/category/<category_id>', methods=['DELETE'])
@jwt_required_with_user()
def delete_category(category_id):
    """删除分类"""
    try:
        user_id = g.user.id
        info = Category.query.filter_by(id=category_id).first()
        if info.user_id != user_id:
            return make_forbidden(msg='无权删除该分类')
        if not info:
            return make_not_found(msg='分类不存在')

        if info.is_delete:
            return make_forbidden(msg='该分类已被删除')

        info.is_delete = True
        info.update_time = now_beijing()
        db.session.commit()

        current_app.logger.info(f"用户 {g.user.id} 删除分类: {category_id}, 分类名: {info.name}")
        return make_success(data={'name': info.name})

    except Exception as e:
        db.session.rollback()
        current_app.logger.error(f"删除分类失败: {e}")
        return make_server_error()


def update_icon(user_id=1):
    """更新icon"""
    import os
    from sqlalchemy.exc import SQLAlchemyError

    icon_path = 'D:/PyScript/separate-projects/frontend/node_modules/bootstrap-icons/icons'
    # icon_path = '/home/icons'

    try:
        icons_to_add = []
        for entry in os.scandir(icon_path):
            if entry.is_file() and entry.name.endswith('.svg'):
                icon_name = entry.name.split('.')[0]
                icon_class = f"bi-{icon_name}"

                info = Icon(
                    user_id=user_id,
                    name='',
                    icon_class=icon_class,
                )
                icons_to_add.append(info)

        # 清空旧数据（根据业务需要决定是否保留）
        Icon.query.filter_by(user_id=user_id).delete()

        # 批量插入新数据
        db.session.bulk_save_objects(icons_to_add)
        db.session.commit()

    except SQLAlchemyError as e:
        db.session.rollback()
        raise RuntimeError(f"Database error occurred: {e}")


@trans_bp.route('/search/icons', methods=['GET'])
@jwt_required_with_user()
def get_icons():
    """返回所有标签"""
    try:
        # 只查询需要的字段，提升性能
        icon_records = Icon.query.with_entities(Icon.icon_class).filter(Icon.is_delete.is_(False)).all()
        icons = [i.icon_class for i in icon_records]
        return make_success(data=icons)
    except db.exc.SQLAlchemyError as e:
        # 捕获数据库异常，防止服务崩溃
        current_app.logger.error(f"查询图标失败: {str(e)}")
        return make_server_error()


@trans_bp.route("/chart/data", methods=["GET"])
@jwt_required_with_user()
def get_chart_data():
    """构建图表数据"""
    user = getattr(g, 'user', None)
    if not user or not hasattr(user, 'id'):
        return make_unauthorized()

    user_id = user.id

    try:
        year = int(request.args.get("year", ""))
    except (TypeError, ValueError):
        return make_validation_error(errors={'year': '无效的参数'})

    if not (1900 <= year <= 2100):
        return make_validation_error(errors={'year': '无效的参数'})

    month = request.args.get("month")
    if month is not None:
        try:
            month = int(month)
            if not (1 <= month <= 12):
                raise ValueError
        except (TypeError, ValueError):
            return make_validation_error(errors={'month': '无效的参数'})

    base_query = db.session.query(Transaction).filter(
        Transaction.user_id == user_id,
        Transaction.is_delete == False
    )

    # 公共时间条件
    time_filters = [extract('year', Transaction.date) == year]
    if month:
        time_filters.append(extract('month', Transaction.date) == month)

    income_expense_case = (
        func.sum(case((Transaction.type == 0, Transaction.amount), else_=0)).label('revenue'),
        func.sum(case((Transaction.type == 1, Transaction.amount), else_=0)).label('expenditure')
    )

    if not month:
        results = base_query.with_entities(
            extract('month', Transaction.date).label('month'),
            *income_expense_case
        ).filter(*time_filters).group_by('month').order_by('month').all()

        data = [
            {
                "month": int(row.month),
                "revenue": float(row.revenue or 0),
                "expenditure": float(row.expenditure or 0)
            }
            for row in results
        ]

    else:
        results = base_query.with_entities(
            Transaction.date.label('date'),
            *income_expense_case
        ).filter(*time_filters).group_by(Transaction.date).order_by(Transaction.date).all()

        data = [
            {
                "date": row.date.isoformat(),
                "revenue": float(row.revenue or 0),
                "expenditure": float(row.expenditure or 0)
            }
            for row in results
        ]

    current_app.logger.info(f"用户 {user_id} 查询图表数据: {year}, {month}")
    return make_success(data=data)
